#pragma once

#include <iostream>
#include <vector>
#include <algorithm>
#include <mutex>
#include <fstream>
#include <jsoncpp/json/json.h>

#include "../comm/Util.hpp"
#include "Model.hpp"
#include "View.hpp"
#include "../comm/Log.hpp"

namespace ns_control
{
    using namespace ns_util;
    using namespace ns_model;
    using namespace ns_view;
    using namespace ns_log;

    //  提供服务的主机
    class Machine
    {
    public:
        friend class LoadBalance;

    public:
        Machine(std::string ip = "", uint16_t port = 0)
            : _ip(ip), _port(port), _load(0), _mtx(nullptr)
        {
        }
        ~Machine()
        {
        }

        // 提升主机负载
        void IncLoad()
        {
            if (_mtx)
                _mtx->lock();
            _load++;
            if (_mtx)
                _mtx->unlock();
        }
        //  减少主机负载
        void DecLoad()
        {
            if (_mtx)
                _mtx->lock();
            _load--;
            if (_mtx)
                _mtx->unlock();
        }

        //  主机负载清0
        void ResetLoad()
        {
            if (_mtx)
                _mtx->lock();
            _load = 0;
            if (_mtx)
                _mtx->unlock();
        }

        // 获取主机负载
        uint64_t GetLoad()
        {
            uint64_t load = 0;
            if (_mtx)
                _mtx->lock();
            load = _load;
            if (_mtx)
                _mtx->unlock();

            return load;
        }

        std::string GetIp()
        {
            return _ip;
        }
        uint16_t GetPort()
        {
            return _port;
        }

    private:
        std::string _ip;  //  编译服务的ip
        uint16_t _port;   //  编译服务的port
        uint64_t _load;   //  负载
        std::mutex *_mtx; //  mutex是禁止拷贝的，用指针
    };

    const std::string machine_list = "./conf/service_machine.conf";

    //  负载均衡模块
    class LoadBalance
    {
    public:
        LoadBalance()
        {
            assert(LoadConf(machine_list));
            LOG(INFO, "配置文件[%s]加载成功\n", machine_list.c_str());
        }

        //  加载配置文件
        bool LoadConf(const std::string &machine_list)
        {
            std::ifstream in(machine_list);
            if (!in.is_open())
            {
                LOG(FATAL, "配置文件[%s]加载失败...\n", machine_list.c_str());
                return false;
            }
            std::string line;
            while (std::getline(in, line))
            {
                std::vector<std::string> addr;
                StringUtil::SplitString(line, &addr, ":");
                if (addr.size() != 2)
                {
                    LOG(WARNING, "[%s]格式错误\n", line.c_str());
                    continue;
                }
                Machine m;
                m._ip = addr[0];
                m._port = stoi(addr[1]);
                m._load = 0;
                m._mtx = new std::mutex();

                online.push_back(machines.size()); //  size==i 对应i号下标
                machines.push_back(m);
            }

            in.close();
            return true;
        }

        //  这里用的负载均衡的算法: 轮询+hash
        //  不断轮询遍历在线的主机，每次都找到负载最小的主机
        bool SmartChoice(int *id, Machine **machine)
        {
            _mtx.lock();

            int online_num = online.size();
            if (online_num == 0)
            {
                _mtx.unlock(); //  返回之前一定要先解锁 死锁警告！

                LOG(FATAL, "所有的后端编译主机全部已经离线！\n");
                return false;
            }

            *id = online[0];
            *machine = &machines[online[0]];

            //  通过遍历 找到负载最小的主机
            uint64_t min_load = machines[online[0]].GetLoad();

            for (int i = 1; i < online_num; i++)
            {
                uint64_t cur_load = machines[online[i]].GetLoad();
                if (min_load > cur_load)
                {
                    min_load = cur_load;
                    *id = online[i];
                    *machine = &machines[online[i]]; //  mutex不支持拷贝构造和赋值重载，所以这里用二级指针要好，而不是在Machine中添加拷贝构造和赋值重载
                }
            }

            _mtx.unlock();
            return true;
        }

        void OffLineMachine(int id)
        {
            _mtx.lock();
            for (auto iter = online.begin(); iter != online.end(); iter++)
            {
                if (*iter == id)
                {
                    machines[id].ResetLoad();   //  将当前主机负载清零
                    online.erase(iter);
                    offline.push_back(id);
                    break; //  不用考虑迭代器失效问题
                }
            }
            _mtx.unlock();
        }

        void OnLineMachine()
        {
            _mtx.lock();
            online.insert(online.end(), offline.begin(), offline.end());
            offline.erase(offline.begin(), offline.end());
            _mtx.unlock();
            LOG(INFO, "所有的主机都已经上线\n");
        }

        void Debug()
        {
            _mtx.lock();
            std::cout << "当前所有在线主机列表：\n";
            for (auto &id : online)
            {
                std::cout << id << " ";
            }
            std::cout << std::endl;
            std::cout << "当前所有离线主机列表：\n";
            for (auto &id : offline)
            {
                std::cout << id << " ";
            }
            std::cout << std::endl;
            _mtx.unlock();
        }

        ~LoadBalance()
        {
        }

    private:
        //  可以提供编译服务的所有主机
        std::vector<Machine> machines;
        //  所有在线的主机
        std::vector<int> online;
        //  所有离线的主机
        std::vector<int> offline;

        //  存在多个执行流并发访问这些资源的问题, 加锁！
        std::mutex _mtx;
    };

    class Control
    {
    public:
        Control()
        {
        }

        void RecoveryMachine()
        {
            _loadbalance.OnLineMachine();
        }

        //  调用model模块加载所有题目，view形成渲染
        bool HtmlForAllQuestions(std::string *html)
        {
            std::vector<Question> questions;
            if (_model.GetAllQuestions(&questions))
            {
                sort(questions.begin(), questions.end(), [](const Question &q1, const Question &q2)
                     { return stoi(q1.number) < stoi(q2.number); });
                _view.RenderingAll(questions, html);
                return true;
            }
            else
            {
                *html = "获取题目列表失败";
                return false;
            }
        }

        //  调用model模块加载指定题目，view形成渲染
        bool HtmlForNumQuestion(const std::string &num, std::string *html)
        {
            Question q;
            if (_model.GetOneQuestions(num, &q))
            {
                _view.RenderingOne(q, html);
                return true;
            }
            else
            {
                *html = "指定题目: " + num + "不存在!";
                return false;
            }
        }

        //  判题功能    number:题号     in_json:用户上传的代码      out_json: 给用户的反馈
        void Judge(const std::string &number, const std::string &in_json, std::string *out_json)
        {
            /*-----数据准备----*/
            Question q;
            _model.GetOneQuestions(number, &q); // 获取指定题目的细节

            //  对in_json反序列化
            Json::Value in_value;
            Json::Reader reader;
            reader.parse(in_json, in_value);

            //  拼接用户提交的代码 + 测试用例, 参考ComplieAndRun::Start
            Json::Value compile_value;
            compile_value["code"] = in_value["code"].asString() + "\n" + q.tail;
            compile_value["input"] = in_value["input"].asString();
            compile_value["cpulimit"] = q.cpulimit;
            compile_value["memlimit"] = q.memlimit;

            // Json::FastWriter writer;    //  生成紧凑的JSON串，没有多余的空格或换行符 适用于网络传输或存储空间有限的应用场景
            Json::StyledWriter writer;                                //  生成格式化的JSON串，增加了空格和换行符，更容易阅读
            std::string compile_string = writer.write(compile_value); //  将compile_value序列化；

            /*-----负载均衡----*/
            while (true)
            {
                int id = 0;
                Machine *m = nullptr;

                if (!_loadbalance.SmartChoice(&id, &m))
                {
                    break;
                }
                //  发起http请求
                httplib::Client cli(m->GetIp(), m->GetPort());
                m->IncLoad(); // 增加负载
                
                LOG(INFO, "选择主机成功: [%s:%d], 当前负载[%d]\n", m->GetIp().c_str(), m->GetPort(), m->GetLoad());

                if (auto res = cli.Post("/compile_and_run", compile_string, "application/json;charset=utf-8"))
                {
                    if (res->status == 200)
                    {
                        *out_json = res->body;
                        m->DecLoad(); //  减少负载
                        LOG(INFO, "编译运行成功\n");
                        break;
                    }
                    m->DecLoad(); //  减少负载
                }
                else
                {
                    //  请求失败
                    LOG(ERROR, "发起http请求失败, [%d][%s:%d]可能已经离线\n", id, m->GetIp().c_str(), m->GetPort());
                    _loadbalance.OffLineMachine(id);
                    _loadbalance.Debug();
                }
            }
        }

        ~Control()
        {
        }

    private:
        Model _model;             //  提供后台数据
        View _view;               //  提供html渲染功能
        LoadBalance _loadbalance; //  核心负载均衡器
    };
}